package com.molichuxing.services.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.molichuxing.services.business.dto.request.create.OfflineBizCreateDto;
import com.molichuxing.services.business.dto.request.create.OfflineOrdersBizCreateDto;
import com.molichuxing.services.business.dto.request.modify.OfflineAuditBizModifyDto;
import com.molichuxing.services.business.dto.request.modify.OrderScheduleOfflineBizModifyDto;
import com.molichuxing.services.business.dto.response.OfflineAuditInfoBizDto;
import com.molichuxing.services.business.dto.response.OfflineBizDto;
import com.molichuxing.services.business.service.OfflineBizService;
import com.molichuxing.services.business.service.PaymentBizService;
import com.molichuxing.services.infrastructure.convertor.Convert;
import com.molichuxing.services.infrastructure.dto.request.create.OfflineCreateDto;
import com.molichuxing.services.infrastructure.dto.request.create.OfflineOrdersCreateDto;
import com.molichuxing.services.infrastructure.dto.request.create.PaymentCreateDto;
import com.molichuxing.services.infrastructure.dto.request.create.PaymentDetailCreateDto;
import com.molichuxing.services.infrastructure.dto.request.modify.OfflineAuditModifyDto;
import com.molichuxing.services.infrastructure.dto.request.modify.PaymentModifyDto;
import com.molichuxing.services.infrastructure.dto.response.OfflineAuditDto;
import com.molichuxing.services.infrastructure.dto.response.OfflineDto;
import com.molichuxing.services.infrastructure.dto.response.OfflineOrdersDto;
import com.molichuxing.services.infrastructure.dto.response.PaymentDto;
import com.molichuxing.services.infrastructure.service.OfflineOrdersService;
import com.molichuxing.services.infrastructure.service.OfflineService;
import com.molichuxing.services.infrastructure.service.PaymentDetailService;
import com.molichuxing.services.infrastructure.service.PaymentService;
import com.molichuxing.services.property.*;
import com.molichuxing.services.utils.PaymentUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 线下支付组合
 * </p>
 *
 * @author zhaopenghui
 * @since 2019-08-21
 */
@Service("offlineBizService")
public class OfflineBizServiceImpl implements OfflineBizService {
    private static final Logger logger = LoggerFactory.getLogger(OfflineBizServiceImpl.class);

    @Autowired
    OfflineService offlineService;
    @Autowired
    OfflineOrdersService offlineOrdersService;
    @Autowired
    PaymentService paymentService;
    @Autowired
    PaymentDetailService paymentDetailService;
    @Autowired
    PaymentBizService paymentBizService;

    /**
     * 线下支付新增
     *
     * @param offlineBizCreateDto
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
    public Boolean createOffline(OfflineBizCreateDto offlineBizCreateDto) throws Exception {
        Assert.notNull(offlineBizCreateDto, "添加线下支付失败");
        List<OfflineOrdersBizCreateDto> offlineOrdersList = offlineBizCreateDto.getOfflineOrdersList();
        scheduleCheck(offlineOrdersList);

        OfflineCreateDto offlineCreateDto = Convert.toOfflineCreateDto(offlineBizCreateDto);
        Integer offlineId = offlineService.createOffline(offlineCreateDto);
        Assert.notNull(offlineId, "添加线下支付失败");

        List<OfflineOrdersCreateDto> offlineOrdersCreateDtoList = Convert.toOfflineOrdersList(offlineId,
                offlineOrdersList);
        Boolean offlineOrdersBoolean = offlineOrdersService.createOfflineOrders(offlineOrdersCreateDtoList);
        if (!offlineOrdersBoolean) {
            return false;
        }
        return true;
    }

    /**
     * 线下支付校验
     *
     * @param offlineOrdersList
     * @throws Exception
     */
    private void scheduleCheck(List<OfflineOrdersBizCreateDto> offlineOrdersList) throws Exception {
        for (OfflineOrdersBizCreateDto offlineOrdersBizCreateDto : offlineOrdersList) {
            Long orderCode = offlineOrdersBizCreateDto.getOrderCode();
            Integer scheduleId = offlineOrdersBizCreateDto.getScheduleId();
            Integer offlineId = offlineOrdersService.getOfflineId(orderCode, scheduleId);
            if (null == offlineId) {
                continue;
            }
            OfflineDto offline = offlineService.getOffline(offlineId);
            if (null == offline) {
                continue;
            }
            Integer auditStatus = offline.getAuditStatus();
            if (OfflineAuditStatusEnum.UNREVISED.getValue() == auditStatus
                    || OfflineAuditStatusEnum.APPROVE.getValue() == auditStatus) {
                throw new IllegalArgumentException("排期已存在,请勿重复添加");
            }
        }
    }

    /**
     * 线下支付审核信息
     *
     * @param orderCode
     * @param scheduleId
     * @return
     * @throws Exception
     */
    @Override
    public OfflineAuditInfoBizDto getOfflineAuditInfo(Long orderCode, Integer scheduleId) throws Exception {

        Integer offlineId = offlineOrdersService.getOfflineId(orderCode, scheduleId);
        if (null == offlineId) {
            return null;
        }

        OfflineAuditDto offlineAuditDto = offlineService.getOfflineAudit(offlineId);
        return Convert.toOfflineAuditInfoBizDto(offlineAuditDto);
    }

    /**
     * 根据订单,排期获取线下支付信息
     *
     * @param orderCode
     * @param scheduleId
     * @return
     * @throws Exception
     */
    @Override
    public OfflineBizDto getOffline(Long orderCode, Integer scheduleId) throws Exception {
        Integer offlineId = offlineOrdersService.getOfflineId(orderCode, scheduleId);
        if (null == offlineId) {
            return null;
        }

        OfflineDto offlineDto = offlineService.getOffline(offlineId);
        return Convert.toOfflineBizDto(offlineDto);
    }

    /**
     * 根据订单号获取是否存在未审核的信息
     *
     * @param orderCode
     * @return true 存在，false不存在
     * @throws Exception
     */
    @Override
    public Boolean getUnAudit(Long orderCode) throws Exception {
        Assert.notNull(orderCode, "订单号不能为空");

        List<Integer> offlineIdList = offlineOrdersService.getOfflineIdList(orderCode);
        if (null == offlineIdList || offlineIdList.isEmpty()) {
            return false;
        }
        // 去重后查询线下支付
        return offlineService.getUnAudit(offlineIdList.stream().distinct().collect(Collectors.toList()));
    }

    /**
     * 还款审核
     *
     * @param offlineAuditBizModifyDto
     * @return
     * @throws Exception
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 36000, rollbackFor = Exception.class)
    public Boolean modifyAuditStatus(OfflineAuditBizModifyDto offlineAuditBizModifyDto,
                                     Map<Integer, OrderScheduleOfflineBizModifyDto> orderScheduleModifyDtoMap) throws Exception {
        OfflineAuditModifyDto offlineAuditModifyDto = Convert.toOfflineAuditModifyDto(offlineAuditBizModifyDto);
        Boolean modifyResult = offlineService.modifyAuditStatus(offlineAuditModifyDto);
        if (!modifyResult) {
            return false;
        }

        Integer auditStatus = offlineAuditBizModifyDto.getAuditStatus();
        if (auditStatus != null && OfflineAuditStatusEnum.APPROVE.getValue() == auditStatus) {
            Integer offlineId = offlineAuditBizModifyDto.getOfflineId();
            List<OfflineOrdersDto> offlineOrdersList = offlineOrdersService.getOfflineOrdersList(offlineId);
            if (null == offlineOrdersList || offlineOrdersList.size() <= 0) {
                return true;
            }

            OfflineDto offline = offlineService.getOffline(offlineId);
            createPayment(offlineOrdersList, offline, orderScheduleModifyDtoMap);
        }
        return true;
    }

    /**
     * 添加支付
     *
     * @param offlineOrdersList
     * @param offline
     * @param orderScheduleModifyDtoMap
     * @throws Exception
     */
    private void createPayment(List<OfflineOrdersDto> offlineOrdersList, OfflineDto offline, Map<Integer, OrderScheduleOfflineBizModifyDto> orderScheduleModifyDtoMap) throws Exception {
        for (OfflineOrdersDto offlineOrdersDto : offlineOrdersList) {
            Integer scheduleId = offlineOrdersDto.getScheduleId();
            if (null == scheduleId) {
                continue;
            }

            OrderScheduleOfflineBizModifyDto orderScheduleOfflineBizModifyDto = orderScheduleModifyDtoMap
                    .get(scheduleId);
            if (null == orderScheduleOfflineBizModifyDto) {
                continue;
            }

            // 根据订单号和排期id查询是否存在
            PaymentDto paymentDto = paymentService.getPaymentByOrder(offlineOrdersDto.getOrderCode(), offlineOrdersDto.getScheduleId());
            if (paymentDto == null) {
                // 不存在，新增
                PaymentCreateDto paymentCreateDto = getPaymentCreateDto(offline, offlineOrdersDto,
                        orderScheduleOfflineBizModifyDto);
                Integer paymentId = paymentService.createPayment(paymentCreateDto);
                // 批量新增支付详情
                List<PaymentDetailCreateDto> paymentDetails = getPaymentDetailList(paymentId, offlineOrdersDto,
                        orderScheduleOfflineBizModifyDto);
                paymentDetailService.createBatch(paymentDetails);
            } else {
                // 存在，修改状态
                PaymentModifyDto paymentModifyDto = new PaymentModifyDto();
                BeanUtils.copyProperties(paymentDto, paymentModifyDto);
                paymentModifyDto.setPayWay(PaymentPayWayEnum.OFFLINE);
                paymentModifyDto.setPayStatus(PaymentStatusEnum.SUCCESS_PAYMENT);
                paymentModifyDto.setPayNumber(PaymentUtil.getPayNumber().toString());
                paymentModifyDto.setThirdPayNumber(offline.getSerialNumber());
                paymentModifyDto.setPayTime(offline.getPayTime());
                if (!paymentService.modifyOffline(paymentModifyDto)) {
                    logger.error("线下支付({})修改支付单失败：({})", offline.getOfflineId(),
                            JSONObject.toJSONString(paymentDto));
                }
            }
        }
    }

    /**
     * 获取还款支付信息
     *
     * @param offline
     * @param offlineOrdersDto
     * @param orderScheduleOfflineBizModifyDto
     * @return
     */
    private PaymentCreateDto getPaymentCreateDto(OfflineDto offline, OfflineOrdersDto offlineOrdersDto,
                                                 OrderScheduleOfflineBizModifyDto orderScheduleOfflineBizModifyDto) throws Exception {
        Integer repayType = offline.getRepayType();
        Integer scheduleId = offlineOrdersDto.getScheduleId();
        Long orderCode = offlineOrdersDto.getOrderCode();

        PaymentCreateDto paymentCreateDto = new PaymentCreateDto();
        paymentCreateDto.setOrderCode(orderCode);
        paymentCreateDto.setPayWay(PaymentPayWayEnum.OFFLINE);
        paymentCreateDto.setTimeStart(offline.getPayTime());
        paymentCreateDto.setTimeEnd(offline.getPayTime());
        paymentCreateDto.setPayTime(offline.getPayTime());
        paymentCreateDto.setPayStatus(PaymentStatusEnum.SUCCESS_PAYMENT);
        if (repayType != null && OfflineRepayTypeEnum.USER.getValue() == repayType) {
            paymentCreateDto.setBusinessType(PaymentBusinessTypeEnum.EXCLUSIVEL);
            Integer userId = orderScheduleOfflineBizModifyDto.getUserId();
            paymentCreateDto.setUserId(userId);
            // 备注
            paymentCreateDto.setRemark(orderScheduleOfflineBizModifyDto.getTotalPeriods() + "期，第"
                    + orderScheduleOfflineBizModifyDto.getPeriods() + "期 租车费");
        } else if (repayType != null && OfflineRepayTypeEnum.DEALER.getValue() == repayType) {
            paymentCreateDto.setBusinessType(PaymentBusinessTypeEnum.DEALER);
            Integer dealerId = orderScheduleOfflineBizModifyDto.getDealerId();
            paymentCreateDto.setUserId(dealerId);
        }
        paymentCreateDto.setCostType(PaymentCostTypeEnum.REPAYMENT);
        paymentCreateDto.setScheduleId(scheduleId);

        BigDecimal feeTotal = BigDecimal.ZERO;
        BigDecimal payable = orderScheduleOfflineBizModifyDto.getPayable();
        feeTotal = feeTotal.add(payable);
        BigDecimal deposit = orderScheduleOfflineBizModifyDto.getDeposit();
        if (deposit.compareTo(BigDecimal.ZERO) > 0) {
            feeTotal = feeTotal.add(deposit);
        }
        paymentCreateDto.setPayAmount(feeTotal);
        paymentCreateDto.setPayNumber(PaymentUtil.getPayNumber().toString());
        paymentCreateDto.setThirdPayNumber(offline.getSerialNumber());
        return paymentCreateDto;
    }

    /**
     * 获取还款支付详情信息
     *
     * @param paymentId
     * @param offlineOrdersDto
     * @param orderScheduleOfflineBizModifyDto
     * @return
     */
    private List<PaymentDetailCreateDto> getPaymentDetailList(Integer paymentId, OfflineOrdersDto offlineOrdersDto,
                                                              OrderScheduleOfflineBizModifyDto orderScheduleOfflineBizModifyDto) {
        Long orderCode = offlineOrdersDto.getOrderCode();

        List<PaymentDetailCreateDto> paymentDetailList = new ArrayList<>();
        BigDecimal payable = orderScheduleOfflineBizModifyDto.getPayable();
        BigDecimal deposit = orderScheduleOfflineBizModifyDto.getDeposit();
        if (payable.compareTo(BigDecimal.ZERO) > 0) {
            PaymentDetailCreateDto paymentDetailCreateDto = new PaymentDetailCreateDto();
            paymentDetailCreateDto.setPaymentId(paymentId);
            paymentDetailCreateDto.setType(PaymentDetailTypeEnum.CART_FEE);
            paymentDetailCreateDto.setAmount(payable);
            paymentDetailCreateDto.setOrderCode(orderCode);
            paymentDetailList.add(paymentDetailCreateDto);
        }
        if (deposit.compareTo(BigDecimal.ZERO) > 0) {
            PaymentDetailCreateDto paymentDetailCreateDto = new PaymentDetailCreateDto();
            paymentDetailCreateDto.setPaymentId(paymentId);
            paymentDetailCreateDto.setType(PaymentDetailTypeEnum.DEPOSIT);
            paymentDetailCreateDto.setAmount(deposit);
            paymentDetailCreateDto.setOrderCode(orderCode);
            paymentDetailList.add(paymentDetailCreateDto);
        }
        return paymentDetailList;
    }

}
